package cn.wolfcode.business.service.impl;

import cn.wolfcode.business.domain.BusBpmnInfo;
import cn.wolfcode.business.domain.BusCarPackageAudit;
import cn.wolfcode.business.domain.BusServiceItem;
import cn.wolfcode.business.domain.vo.CarPackageAuditVo;
import cn.wolfcode.business.domain.vo.HistoryVo;
import cn.wolfcode.business.mapper.BusCarPackageAuditMapper;
import cn.wolfcode.business.service.IBusBpmnInfoService;
import cn.wolfcode.business.service.IBusCarPackageAuditService;
import cn.wolfcode.business.service.IBusServiceItemService;
import cn.wolfcode.common.utils.DateUtils;
import cn.wolfcode.common.utils.PageUtils;
import cn.wolfcode.common.utils.SecurityUtils;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 套餐审核Service业务层处理
 * 
 * @author wolfcode
 * @date 2025-08-27
 */
@Service
public class BusCarPackageAuditServiceImpl implements IBusCarPackageAuditService 
{
    @Autowired
    private BusCarPackageAuditMapper busCarPackageAuditMapper;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private IBusServiceItemService busServiceItemService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private IBusBpmnInfoService busBpmnInfoService;

    /**
     * 查询套餐审核
     * 
     * @param id 套餐审核主键
     * @return 套餐审核
     */
    @Override
    public BusCarPackageAudit selectBusCarPackageAuditById(Long id)
    {
        return busCarPackageAuditMapper.selectBusCarPackageAuditById(id);
    }

    /**
     * 查询套餐审核列表
     * 
     * @param busCarPackageAudit 套餐审核
     * @return 套餐审核
     */
    @Override
    public List<BusCarPackageAudit> selectBusCarPackageAuditList(BusCarPackageAudit busCarPackageAudit)
    {
        return busCarPackageAuditMapper.selectBusCarPackageAuditList(busCarPackageAudit);
    }

    /**
     * 新增套餐审核
     * 
     * @param busCarPackageAudit 套餐审核
     * @return 结果
     */
    @Override
    public int insertBusCarPackageAudit(BusCarPackageAudit busCarPackageAudit)
    {
        busCarPackageAudit.setCreateTime(DateUtils.getNowDate());
        return busCarPackageAuditMapper.insertBusCarPackageAudit(busCarPackageAudit);
    }

    /**
     * 修改套餐审核
     * 
     * @param busCarPackageAudit 套餐审核
     * @return 结果
     */
    @Override
    public int updateBusCarPackageAudit(BusCarPackageAudit busCarPackageAudit)
    {
        return busCarPackageAuditMapper.updateBusCarPackageAudit(busCarPackageAudit);
    }

    /**
     * 批量删除套餐审核
     * 
     * @param ids 需要删除的套餐审核主键
     * @return 结果
     */
    @Override
    public int deleteBusCarPackageAuditByIds(Long[] ids)
    {
        return busCarPackageAuditMapper.deleteBusCarPackageAuditByIds(ids);
    }

    /**
     * 删除套餐审核信息
     * 
     * @param id 套餐审核主键
     * @return 结果
     */
    @Override
    public int deleteBusCarPackageAuditById(Long id)
    {
        return busCarPackageAuditMapper.deleteBusCarPackageAuditById(id);
    }

    /**
     * 根据快照表 id 更新快照表的流程实例 id
     * @param id 快照表 id
     * @param processInstanceId 流程实例 id
     */
    @Override
    public void updateInstanceIdById(Long id, String processInstanceId) {
        busCarPackageAuditMapper.updateInstanceIdById(id, processInstanceId);
    }

    /**
     * 查看流程图
     * @param instanceId 流程实例 id
     * @return 图片的二进制流
     */
    @Override
    @Transactional
    public InputStream getProcessImg(Long instanceId) {
        // 参数合理化验证
        if(instanceId == null){
            throw new RuntimeException("非法参数");
        }
        // 创建可以绘制图的类
        DefaultProcessDiagramGenerator processDiagramGenerator = new DefaultProcessDiagramGenerator();
        // 根据流程实例 ID 查询流程实例对象
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(instanceId.toString())
                .singleResult();
        BpmnModel bpmnModel = null;
        List<String> activeActivityIds = new ArrayList<>();
        if(processInstance == null){
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(instanceId.toString())
                    .singleResult();
            bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
        } else {
            bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
            // 获取当前活动的节点
            activeActivityIds = runtimeService.getActiveActivityIds(instanceId.toString());
        }
        // 根据流程定义获取 Model 对象

        /**
         * 第一个参数： 模型对象 bpmnModel
         * 第二个参数： 高亮的节点
         * 第三个参数： 高亮的连线
         * 第四五六：字体
         */
        InputStream inputStream = processDiagramGenerator.generateDiagram(
                bpmnModel,
                activeActivityIds,
                Collections.emptyList(),
                "宋体",
                "宋体",
                "宋体"
        );
        return inputStream;
    }

    /**
     * 流程撤销
     * @param id 套餐审核对象（快照表）
     * @return 成功或失败
     */
    @Override
    @Transactional
    public int processCancel(Long id) {
        // 参数合理化验证
        if(id == null){
            throw new RuntimeException("非法参数");
        }
        // 根据传入的 id 查询套餐审批对象（快照表）。
        BusCarPackageAudit busCarPackageAudit = this.selectBusCarPackageAuditById(id);
        if(busCarPackageAudit == null){
            throw new RuntimeException("非法参数");
        }
        // 根据查询出来的套餐审批对象查询对应的 服务项对象
        BusServiceItem busServiceItem = busServiceItemService.selectBusServiceItemById(busCarPackageAudit.getServiceItemId());
        if(busServiceItem == null){
            throw new RuntimeException("非法参数");
        }
        // 验证 在套餐审核对象和服务项对象中。必须是审批中才可以进行撤销操作。
        if(!(BusCarPackageAudit.STATUS_IN_PROGRESS.equals(busCarPackageAudit.getStatus()) &&
                BusServiceItem.AUDITSTATUS_AUDITING.equals(busServiceItem.getAuditStatus()))){
            throw new RuntimeException("必须处于审批中的流程才可以进行撤销操作");
        }
        // 删除流程实例
        runtimeService.deleteProcessInstance(busCarPackageAudit.getInstanceId(),"流程撤销");
        // 将 套餐审批对象（快照表） 状态修改为流程撤销 (这种没有使用前台传递的数据，就不存在安全问题，所以可以使用全量更新)
        busCarPackageAudit.setStatus(BusCarPackageAudit.STATUS_CANCEL);
        busCarPackageAuditMapper.updateBusCarPackageAudit(busCarPackageAudit);
        // 将 服务项对象状态修改为 初始化
        return busServiceItemService.changeAuditStatus(busServiceItem.getId(),BusServiceItem.AUDITSTATUS_INIT);
    }

    /**
     * 根据历史流程实例 id 查询历史任务列表
     * @param instanceId 历史流程实例 id
     * @return 历史任务列表
     */
    @Override
    @Transactional
    public List<HistoryVo> getHistoryTaskList(Long instanceId) {
        // 参数合理化验证
        if (instanceId == null) {
            throw new RuntimeException("非法参数");
        }
        // 根据历史实例 id 查询历史任务集合列表
        List<HistoricTaskInstance> historyTaskList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(instanceId.toString())
                .list();
        if(historyTaskList == null || historyTaskList.isEmpty()){
            throw new RuntimeException("非法参数");
        }
        // jdk 8 新特性的遍历方式  使用的是 stream 流 . map   最终可以将每次 return 的结果直接封装到返回的集合中。
        List<HistoryVo> historyVoList = historyTaskList.stream().map(task -> {
            HistoryVo vo = new HistoryVo();
            vo.setTaskName(task.getName());     // 任务名称
            vo.setStartTime(task.getStartTime());       // 开始时间
            // 若存在结束时间和总耗时，我们在进行封装
            if(task.getEndTime() != null && task.getDurationInMillis() != null){
                vo.setEndTime(task.getEndTime());        // 结束时间
                vo.setDurationInMillis(task.getDurationInMillis() / 1000 + "s");  // 总耗时
            }
            List<Comment> taskComments = taskService.getTaskComments(task.getId());
            if(taskComments != null && !taskComments.isEmpty()){
                vo.setComment(taskComments.get(0).getFullMessage());
            }
            return vo;
        }).collect(Collectors.toList());
        return historyVoList;
    }

    /**
     * 查询我的代办任务
     * @param busCarPackageAudit 业务表数据
     * @return 我的代办列表
     */
    @Override
    @Transactional
    public List<BusCarPackageAudit> todoTaskList(BusCarPackageAudit busCarPackageAudit) {
        // 我们需要获取登录用户 id
        Long userId = SecurityUtils.getUserId();
        // 获取到流程定义的 Key --> BpmnInfo 对象中存在  --> 获取 BpmnInfo 对象。
        BusBpmnInfo bpmnInfo = busBpmnInfoService.selectBpmnInfoByType(BusCarPackageAudit.FLOW_AUDIT_TYPE);
        String processDefinitionKey = bpmnInfo.getProcessDefinitionKey();
        // 我们就可以根据 processDefinitionKey 和 version 还有用户 id 查询出对应用户的代办任务。
        // 此时我们的目的已经达成，拿到了用户的代办任务。但是我们需要展示的是业务表中的数据
        List<Task> taskList = taskService.createTaskQuery()
                .processDefinitionKey(processDefinitionKey)
                .taskAssignee(userId.toString())
                .list();
        // 没查到任务说明没有代办任务直接返回空集合即可。
        if(taskList == null || taskList.isEmpty()){  // taskList == null || taskList.size() == 0
            return Collections.emptyList();
        }
        // 遍历任务，拿到任务对应的流程实例 instanceId。我们就拿到了当前用户对应任务的所有流程实例 id 的 集合。
        List<String> processInstanceIdList = taskList.stream().map(Task::getProcessInstanceId).toList();
        // 可以通过流程实例 id 的集合去 套餐审批表中（快照表） 根据 instanceId 查询出每一条业务信息
        // 此时我们需要对查询的信息进行分页处理！
        PageUtils.startPage();
        // 增加额外条件进行查询即可 (额外参数)
        if(processInstanceIdList != null &&  !processInstanceIdList.isEmpty()){
            busCarPackageAudit.getParams().put("processInstanceIdList", processInstanceIdList);
        }
        return busCarPackageAuditMapper.selectBusCarPackageAuditList(busCarPackageAudit);
    }

    @Override
    @Transactional
    public Integer audit(CarPackageAuditVo vo) {
        if(vo == null || vo.getId() == null || vo.getAuditStatus() == null){
            throw new RuntimeException("非法参数");
        }
        // 根据传递的 套餐审批 id（快照表 id） 查询出 套餐审批对象
        BusCarPackageAudit busCarPackageAudit = this.selectBusCarPackageAuditById(vo.getId());
        if(busCarPackageAudit == null || busCarPackageAudit.getInstanceId() == null){
            throw new RuntimeException("非法参数");
        }
        if(!BusCarPackageAudit.STATUS_IN_PROGRESS.equals(busCarPackageAudit.getStatus())){
            throw new RuntimeException("任务状态需要处于审批中才可以进行审批操作");
        }
        // 获取对象中的 instanceId
        String instanceId = busCarPackageAudit.getInstanceId();
        // 获取当前用户 id
        Long userId = SecurityUtils.getUserId();
        // 根据流程实例 id 和 当前用户 id 查询出当前用户准备审批的正在执行的任务。
        Task task = taskService.createTaskQuery()
                .processInstanceId(instanceId)
                .taskAssignee(userId.toString())
                .singleResult();
        if(task == null){
             throw new RuntimeException("非法参数");
        }
        // 拼接批注信息   审批意见：[xx:审批通过||审批拒绝,批注信息:xxxx]
        StringBuilder msg = new StringBuilder("审批意见:[");
        msg.append(SecurityUtils.getUsername());
        msg.append(":");
        boolean auditStatus = vo.getAuditStatus() == 2;
        if(auditStatus){
            // 审批通过
            msg.append("审批通过,批注信息:");
        } else {
            // 审批拒绝
            msg.append("审批拒绝,批注信息:");
        }
        msg.append(vo.getAuditInfo()).append("]");
        // 使用 Activiti API 来添加批注信息
        taskService.addComment(task.getId(),instanceId,msg.toString());
        // 为线程变量赋值
        Map<String,Object> params =  new HashMap<>();
        params.put("shopOwner",auditStatus);
        params.put("disCountPrice",busCarPackageAudit.getServiceItemPrice().longValue());
        // 完成任务
        taskService.complete(task.getId(),params);

        // 审批线
        // 判定审批者是同意还是拒绝
        boolean flag = true;
        if(auditStatus){
            // 审批同意
            // 查询是否还存在下一个任务
            List<Task> nextTaskList = taskService.createTaskQuery()
                    .processInstanceId(instanceId)
                    .list();
            if(nextTaskList == null || nextTaskList.isEmpty()){
                // 说明没有下一个任务了。该流程已经正常走完结束了。
                busCarPackageAudit.setStatus(BusCarPackageAudit.STATUS_PASS);
                int auditCount = busCarPackageAuditMapper.updateBusCarPackageAudit(busCarPackageAudit);
                // 修改其对应的 服务项表中的 状态  -->  审批拒绝
                int itemCount = busServiceItemService.changeAuditStatus(busCarPackageAudit.getServiceItemId(), BusServiceItem.AUDITSTATUS_APPROVED);
                flag = (auditCount != 0 && itemCount != 0);
            }
        } else {
            // 审批拒绝
            // 修改 套餐审批列表（快照表） 状态  -->  审批拒绝
            // 该数据是从数据库中查询到的，并没有接收前端信息，所以可以直接使用全量更新（不建议）
            busCarPackageAudit.setStatus(BusCarPackageAudit.STATUS_REJECT);
            int auditCount = busCarPackageAuditMapper.updateBusCarPackageAudit(busCarPackageAudit);
            // 修改其对应的 服务项表中的 状态  -->  审批拒绝
            int itemCount = busServiceItemService.changeAuditStatus(busCarPackageAudit.getServiceItemId(),BusServiceItem.AUDITSTATUS_REPLY);
            flag = (auditCount != 0 && itemCount != 0);
        }
        return flag ? 1 : 0;
    }

    /**
     * 查询已办任务列表
     * @param busCarPackageAudit 业务表数据
     * @return 我的已办列表
     */
    @Override
    @Transactional
    public List<BusCarPackageAudit> doneTaskList(BusCarPackageAudit busCarPackageAudit) {
        // 参数合理化验证
        if(busCarPackageAudit == null){
            throw new RuntimeException("非法参数");
        }
        // 获取当前登录用户
        Long userId = SecurityUtils.getUserId();
        // 获取 BpmnInfo 表中的 Key
        BusBpmnInfo bpmnInfo = busBpmnInfoService.selectBpmnInfoByType(BusCarPackageAudit.FLOW_AUDIT_TYPE);
        String processDefinitionKey = bpmnInfo.getProcessDefinitionKey();
        // 查询当前用户的历史任务集合
        List<HistoricTaskInstance> historyTaskList = historyService.createHistoricTaskInstanceQuery()
                .processDefinitionKey(processDefinitionKey)
                .taskAssignee(userId.toString())
                .finished() // 已完成任务
                .list();
        // 接下来我们通过每个任务获取到流程实例 id 的集合.
        List<String> processInstanceIdList = historyTaskList.stream().map(HistoricTaskInstance::getProcessInstanceId).toList();
        // 数据验证
        if(processInstanceIdList == null || processInstanceIdList.isEmpty()){
            return Collections.emptyList();
        }
        // 若能执行到这里，说明不是空集合。存在历史任务
        // 添加额外参数
        busCarPackageAudit.getParams().put("processInstanceIdList", processInstanceIdList);
        // 分页
        PageUtils.startPage();
        // 根据流程实例 id 的集合 批量查询套餐记录中 我的已办 数据。
        return busCarPackageAuditMapper.selectBusCarPackageAuditList(busCarPackageAudit);
    }
}
